<template>
  <transition name="fade">
    <div
      v-show="visible"
      class="back-to-top"
      :class="{ 'back-to-top--fixed': isFixed }"
      @click="scrollToTop"
    >
      <el-button
        circle
        :size="size"
        :type="type"
        class="back-to-top__button"
      >
        <el-icon>
          <ArrowUp />
        </el-icon>
      </el-button>
      
      <!-- 进度环 -->
      <svg
        v-if="showProgress"
        class="progress-ring"
        :width="progressSize"
        :height="progressSize"
      >
        <circle
          class="progress-ring__circle"
          :stroke="progressColor"
          stroke-width="2"
          fill="transparent"
          :r="progressRadius"
          :cx="progressSize / 2"
          :cy="progressSize / 2"
          :style="progressStyle"
        />
      </svg>
    </div>
  </transition>
</template>

<script setup>
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { ArrowUp } from '@element-plus/icons-vue'

// Props
const props = defineProps({
  // 显示阈值（滚动距离）
  visibilityHeight: {
    type: Number,
    default: 400
  },
  // 按钮尺寸
  size: {
    type: String,
    default: 'default',
    validator: (value) => ['large', 'default', 'small'].includes(value)
  },
  // 按钮类型
  type: {
    type: String,
    default: 'primary'
  },
  // 是否固定定位
  isFixed: {
    type: Boolean,
    default: true
  },
  // 距离右边的距离
  right: {
    type: String,
    default: '40px'
  },
  // 距离底部的距离
  bottom: {
    type: String,
    default: '40px'
  },
  // 滚动目标元素
  target: {
    type: String,
    default: ''
  },
  // 是否显示进度环
  showProgress: {
    type: Boolean,
    default: true
  },
  // 进度环颜色
  progressColor: {
    type: String,
    default: '#409eff'
  },
  // 滚动动画时长
  duration: {
    type: Number,
    default: 500
  }
})

// Emits
const emit = defineEmits(['click'])

// 响应式数据
const visible = ref(false)
const scrollTop = ref(0)
const scrollHeight = ref(0)
const clientHeight = ref(0)

// 计算属性
const scrollTarget = computed(() => {
  if (props.target) {
    return document.querySelector(props.target)
  }
  return window
})

const progressSize = computed(() => {
  const sizeMap = {
    large: 48,
    default: 40,
    small: 32
  }
  return sizeMap[props.size] || 40
})

const progressRadius = computed(() => {
  return (progressSize.value - 4) / 2
})

const scrollProgress = computed(() => {
  if (scrollHeight.value <= clientHeight.value) return 0
  return Math.min(scrollTop.value / (scrollHeight.value - clientHeight.value), 1)
})

const progressStyle = computed(() => {
  const circumference = 2 * Math.PI * progressRadius.value
  const offset = circumference * (1 - scrollProgress.value)
  
  return {
    strokeDasharray: circumference,
    strokeDashoffset: offset,
    transform: 'rotate(-90deg)',
    transformOrigin: '50% 50%'
  }
})

// 方法
const handleScroll = () => {
  if (scrollTarget.value === window) {
    scrollTop.value = window.pageYOffset || document.documentElement.scrollTop
    scrollHeight.value = document.documentElement.scrollHeight
    clientHeight.value = window.innerHeight
  } else {
    const target = scrollTarget.value
    if (target) {
      scrollTop.value = target.scrollTop
      scrollHeight.value = target.scrollHeight
      clientHeight.value = target.clientHeight
    }
  }
  
  visible.value = scrollTop.value >= props.visibilityHeight
}

const scrollToTop = () => {
  emit('click')
  
  const target = scrollTarget.value
  const startTime = Date.now()
  const startScrollTop = scrollTop.value
  
  const animateScroll = () => {
    const elapsed = Date.now() - startTime
    const progress = Math.min(elapsed / props.duration, 1)
    
    // 使用缓动函数
    const easeOutCubic = 1 - Math.pow(1 - progress, 3)
    const currentScrollTop = startScrollTop * (1 - easeOutCubic)
    
    if (target === window) {
      window.scrollTo(0, currentScrollTop)
    } else {
      target.scrollTop = currentScrollTop
    }
    
    if (progress < 1) {
      requestAnimationFrame(animateScroll)
    }
  }
  
  requestAnimationFrame(animateScroll)
}

// 生命周期
onMounted(() => {
  const target = scrollTarget.value
  
  if (target === window) {
    window.addEventListener('scroll', handleScroll, { passive: true })
  } else if (target) {
    target.addEventListener('scroll', handleScroll, { passive: true })
  }
  
  // 初始检查
  handleScroll()
})

onUnmounted(() => {
  const target = scrollTarget.value
  
  if (target === window) {
    window.removeEventListener('scroll', handleScroll)
  } else if (target) {
    target.removeEventListener('scroll', handleScroll)
  }
})
</script>

<style lang="scss" scoped>
.back-to-top {
  position: relative;
  display: inline-block;
  
  &--fixed {
    position: fixed;
    right: v-bind(right);
    bottom: v-bind(bottom);
    z-index: 1000;
  }
}

.back-to-top__button {
  position: relative;
  z-index: 2;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transition: all 0.3s ease;
  
  &:hover {
    transform: translateY(-2px);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);
  }
  
  &:active {
    transform: translateY(0);
  }
}

.progress-ring {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  pointer-events: none;
}

.progress-ring__circle {
  transition: stroke-dashoffset 0.1s ease;
}

.fade-enter-active,
.fade-leave-active {
  transition: all 0.3s ease;
}

.fade-enter-from {
  opacity: 0;
  transform: translateY(20px) scale(0.8);
}

.fade-leave-to {
  opacity: 0;
  transform: translateY(20px) scale(0.8);
}

@media (max-width: 768px) {
  .back-to-top--fixed {
    right: 20px;
    bottom: 20px;
  }
}
</style>
